#ifndef __TDP2_STRING_HPP__
#define __TDP2_STRING_HPP__

#include "tdp2.hpp"
#include "tdp2_vector.hpp"
#include "tdp2_allocator.hpp"
#include "tdp2_error.hpp"

namespace TDP
{
	class PanicHandler;

	template<class _Tchar>
	class DynString
	{
	private:
		DynString(const DynString &ds) { }		// Copies and assigns are private since they can't access the PH
		DynString<_Tchar> &operator = (const _Tchar *cstr);
		DynString<_Tchar> &operator = (const DynString<_Tchar> &rs);

	protected:
		static const LargeUInt NumStaticChars_k = 128;

		_Tchar _staticBuf[NumStaticChars_k+1];
		Containers::Vector<_Tchar> _dynBuf;
		LargeUInt _length;
		RDX::ObjectManagement::Allocator _alloc;

		bool TryResize(LargeUInt length);
		bool TryDisplace(const _Tchar *chars1, LargeUInt length1, const _Tchar *chars2, LargeUInt length2);
		void Displace(const PanicHandler *ph, const _Tchar *chars1, LargeUInt length1, const _Tchar *chars2, LargeUInt length2);

	public:
		
		DynString();
		DynString(const PanicHandler *ph, const DynString<_Tchar> &str);
		DynString(const PanicHandler *ph, const _Tchar *cstr);
		DynString(const PanicHandler *ph, const _Tchar *cstr, LargeUInt len);
		DynString(const PanicHandler *ph, const _Tchar *cstr1, LargeUInt len1, const _Tchar *cstr2, LargeUInt len2);

		explicit DynString(const RDX::ObjectManagement::Allocator &alloc);
		DynString(const PanicHandler *ph, const RDX::ObjectManagement::Allocator &alloc, const _Tchar *cstr);
		DynString(const PanicHandler *ph, const RDX::ObjectManagement::Allocator &alloc, const _Tchar *cstr, LargeUInt len);
		DynString(const PanicHandler *ph, const RDX::ObjectManagement::Allocator &alloc, const _Tchar *cstr1, LargeUInt len1, const _Tchar *cstr2, LargeUInt len2);

		~DynString();

		const _Tchar &operator [](LargeUInt idx) const;
		_Tchar operator [](LargeInt idx) const;

		void Set(const PanicHandler *ph, const DynString<_Tchar> &str);
		bool TrySet(const DynString<_Tchar> &str);

		void Set(const PanicHandler *ph, const _Tchar *cstr);
		bool TrySet(const _Tchar *cstr);

		void Set(const PanicHandler *ph, const _Tchar *cstr, LargeUInt len);
		bool TrySet(const _Tchar *cstr, LargeUInt len);

		void SetConcat(const PanicHandler *ph, const DynString<_Tchar> &ls, const DynString<_Tchar> &rs);
		bool TrySetConcat(const DynString<_Tchar> &ls, const DynString<_Tchar> &rs);

		void SetConcat(const PanicHandler *ph, const _Tchar *cstr, const DynString<_Tchar> &rs);
		bool TrySetConcat(const _Tchar *cstr, const DynString<_Tchar> &rs);

		void SetConcat(const PanicHandler *ph, const DynString<_Tchar> &ls, const _Tchar *cstr);
		bool TrySetConcat(const DynString<_Tchar> &ls, const _Tchar *cstr);

		void SetConcat(const PanicHandler *ph, const _Tchar *lcstr, const _Tchar *rcstr);
		bool TrySetConcat(const _Tchar *lcstr, const _Tchar *rcstr);
		
		void Append(const PanicHandler *ph, const DynString<_Tchar> &rs);
		bool TryAppend(const DynString<_Tchar> &rs);

		void Append(const PanicHandler *ph, const _Tchar *rs);
		bool TryAppend(const _Tchar *rs);

		void Append(const PanicHandler *ph, const _Tchar &rs);
		bool TryAppend(const _Tchar &rs);

		bool operator == (const DynString<_Tchar> &rs) const;
		bool operator == (const _Tchar *rs) const;
		friend bool operator == (const _Tchar *cstr, const DynString<_Tchar> &rs);

		bool operator != (const DynString<_Tchar> &rs) const;
		bool operator != (const _Tchar *rs) const;
		friend bool operator != (const _Tchar *cstr, const DynString<_Tchar> &rs);

		void SetSubString(const PanicHandler *ph, const DynString<_Tchar> &str, LargeUInt start, LargeUInt len);
		bool TrySetSubString(const DynString<_Tchar> &str, LargeUInt start, LargeUInt len);

		void SetSubString(const PanicHandler *ph, const DynString<_Tchar> &str, LargeUInt start);
		bool TrySetSubString(const DynString<_Tchar> &str, LargeUInt start);

		float ToFloat() const;
		int ToInt() const;
		void SetToInt(const PanicHandler *ph, LargeInt i);
		void SetToInt(const PanicHandler *ph, LargeUInt i);

		const _Tchar *Chars() const;
		_Tchar *Chars();
		const RDX::ObjectManagement::Allocator &Allocator() const;
		LargeUInt Length() const;

		static LargeUInt CStrLen(const _Tchar *chars);
		static UInt32 Hash32(UInt32 hash, const DynString<_Tchar> *ptr);

		static bool TryCopy(DynString<_Tchar> &ls, const DynString<_Tchar> &rs);
	};
};

#include <string.h>
#include <stdlib.h>
#include "tdp2_allocator.hpp"
#include "tdp2_hash.hpp"

namespace TDP
{
	template<class _Tchar>
	inline DynString<_Tchar>::DynString()
		: _dynBuf(TDP::Memory::DefaultAllocator())
	{
		_staticBuf[0] = _Tchar(0);
		_length = 0;
		_alloc = TDP::Memory::DefaultAllocator();
	}

	template<class _Tchar>
	inline DynString<_Tchar>::DynString(const PanicHandler *ph, const _Tchar *chars, LargeUInt len)
		: _dynBuf(TDP::Memory::DefaultAllocator())
	{
		_length = 0;
		_alloc = TDP::Memory::DefaultAllocator();
		Displace(ph, chars, len, NULL, 0);
	}

	template<class _Tchar>
	inline DynString<_Tchar>::DynString(const PanicHandler *ph, const _Tchar *chars1, LargeUInt len1,
		const _Tchar *chars2, LargeUInt len2)
		: _dynBuf(TDP::Allocator::Default())
	{
		_length = 0;
		_alloc = TDP::Allocator::Default();
		Displace(ph, chars1, len1, chars2, len2);
	}


	template<class _Tchar>
	inline DynString<_Tchar>::DynString(const PanicHandler *ph, const _Tchar *chars)
		: _dynBuf(TDP::Allocator::Default())
	{
		_length = 0;
		_alloc = TDP::Allocator::Default();
		Displace(ph, chars, CStrLen(chars), NULL, 0);
	}

	template<class _Tchar>
	inline DynString<_Tchar>::DynString(const RDX::ObjectManagement::Allocator &alloc)
		: _dynBuf(alloc)
	{
		_staticBuf[0] = _Tchar(0);
		_length = 0;
		_alloc = alloc;
	}

	template<class _Tchar>
	inline DynString<_Tchar>::DynString(const PanicHandler *ph, const RDX::ObjectManagement::Allocator &alloc, const _Tchar *chars, LargeUInt len)
		: _dynBuf(alloc)
	{
		_length = 0;
		_alloc = alloc;
		Displace(ph, chars, len, NULL, 0);
	}

	template<class _Tchar>
	inline DynString<_Tchar>::DynString(const PanicHandler *ph, const RDX::ObjectManagement::Allocator &alloc, const _Tchar *chars1, LargeUInt len1,
		const _Tchar *chars2, LargeUInt len2)
		: _dynBuf(alloc)
	{
		_length = 0;
		_alloc = alloc;
		Displace(chars1, len1, chars2, len2);
	}


	template<class _Tchar>
	inline DynString<_Tchar>::DynString(const PanicHandler *ph, const RDX::ObjectManagement::Allocator &alloc, const _Tchar *chars)
		: _dynBuf(alloc)
	{
		_length = 0;
		_alloc = alloc;
		Displace(ph, chars, CStrLen(chars), NULL, 0);
	}

	template<class _Tchar>
	inline DynString<_Tchar>::DynString(const PanicHandler *ph, const DynString<_Tchar> &cpy)
		: _dynBuf(cpy._alloc)
	{
		_length = 0;
		_alloc = cpy._alloc;
		Displace(ph, cpy.Chars(), cpy._length, NULL, 0);
	}

	template<class _Tchar>
	inline DynString<_Tchar>::~DynString()
	{
	}

	template<class _Tchar>
	const _Tchar &DynString<_Tchar>::operator [](LargeUInt idx) const
	{
		return Chars()[idx];
	}

	template<class _Tchar>
	inline _Tchar DynString<_Tchar>::operator [](LargeInt idx) const
	{
		return Chars()[idx];
	}

	template<class _Tchar>
	inline void DynString<_Tchar>::Set(const PanicHandler *ph, const DynString<_Tchar> &str)
	{
		Displace(ph, str.Chars(), str._length, NULL, 0);
	}

	template<class _Tchar>
	inline bool DynString<_Tchar>::TrySet(const DynString<_Tchar> &str)
	{
		return TryDisplace(str.Chars(), str._length, NULL, 0);
	}

	template<class _Tchar>
	inline void DynString<_Tchar>::Set(const PanicHandler *ph, const _Tchar *cstr)
	{
		Displace(ph, cstr, CStrLen(cstr), NULL, 0);
	}

	template<class _Tchar>
	inline bool DynString<_Tchar>::TrySet(const _Tchar *cstr)
	{
		return TryDisplace(cstr, CStrLen(cstr), NULL, 0);
	}

	template<class _Tchar>
	inline void DynString<_Tchar>::Set(const PanicHandler *ph, const _Tchar *cstr, LargeUInt len)
	{
		Displace(ph, cstr, len, NULL, 0);
	}

	template<class _Tchar>
	inline bool DynString<_Tchar>::TrySet(const _Tchar *cstr, LargeUInt len)
	{
		return TryDisplace(cstr, len, NULL, 0);
	}
		
	template<class _Tchar>
	inline const _Tchar *DynString<_Tchar>::Chars() const
	{
		if(_length <= NumStaticChars_k)
			return _staticBuf;
		return _dynBuf;
	}
		
	template<class _Tchar>
	inline _Tchar *DynString<_Tchar>::Chars()
	{
		if(_length <= NumStaticChars_k)
			return _staticBuf;
		return _dynBuf;
	}
		
	template<class _Tchar>
	inline LargeUInt DynString<_Tchar>::Length() const
	{
		return _length;
	}
	
	template<class _Tchar>
	inline bool DynString<_Tchar>::TryCopy(DynString<_Tchar> &ls, const DynString<_Tchar> &rs)
	{
		new (&ls) DynString<_Tchar>(rs._alloc);
		return ls.TrySet(rs);
	}


	template<class _Tchar>
	inline void DynString<_Tchar>::SetSubString(const PanicHandler *ph, const DynString<_Tchar> &str, LargeUInt start, LargeUInt len)
	{
		if(start > str._length)
			start = str._length;
		if(len > str._length - start)
			len = str._length - start;
		Displace(ph, str.Chars() + start, len, NULL, 0);
	}

	template<class _Tchar>
	inline void DynString<_Tchar>::SetSubString(const PanicHandler *ph, const DynString<_Tchar> &str, LargeUInt start)
	{
		if(start > str._length)
			start = str._length;
		Displace(ph, str.Chars() + start, str._length - start, NULL, 0);
	}

	inline float DynString<char>::ToFloat() const
	{
		return static_cast<float>(atof(Chars()));
	}

	inline float DynString<wchar_t>::ToFloat() const
	{
		return static_cast<float>(_wtof(Chars()));
	}
	
	template<class _Tchar>
	inline int DynString<_Tchar>::ToInt() const
	{
		LargeUInt l = _length;
		const _Tchar *chars = Chars();
		bool negate = false;
		int rv = 0;
		if(chars[0] == '-')
		{
			chars++;
			l--;
			negate = true;
		}
		while(l)
		{
			_Tchar c = *chars;
			if(c < '0' || c > '9')
				return 0;
			rv = rv * 10 + c - '0';
			chars++;
			l--;
		}
		if(negate)
			return -rv;
		return rv;
	}

	template<class _Tchar>
	void DynString<_Tchar>::SetToInt(const PanicHandler *ph, LargeInt i)
	{
		const int maxDigits = sizeof(LargeInt) * 3 + 2;
		_Tchar chars[maxDigits];
		LargeUInt nDigits = 0;
		bool negate = false;
		if(i < 0)
		{
			negate = true;
			i = -i;
		}
		while(i)
		{
			nDigits++;
			chars[maxDigits-nDigits] = static_cast<_Tchar>((i % 10) + '0');
			i = i / 10;
		}
		if(nDigits == 0)
		{
			chars[0] = static_cast<_Tchar>('0');
			Set(ph, chars, 1);
			return;
		}
		if(negate)
		{
			nDigits++;
			chars[maxDigits-nDigits] = static_cast<_Tchar>('-');
		}

		Set(ph, chars + maxDigits - nDigits, nDigits);
	}

	template<class _Tchar>
	void DynString<_Tchar>::SetToInt(const PanicHandler *ph, LargeUInt i)
	{
		const int maxDigits = sizeof(LargeInt) * 3 + 2;
		_Tchar chars[maxDigits];
		LargeUInt nDigits = 0;
		while(i)
		{
			nDigits++;
			chars[maxDigits-nDigits] = static_cast<_Tchar>((i % 10) + '0');
			i = i / 10;
		}
		if(nDigits == 0)
		{
			chars[0] = static_cast<_Tchar>('0');
			Set(ph, chars, 1);
			return;
		}
		Set(ph, chars + maxDigits - nDigits, nDigits);
	}


	template<class _Tchar>
	inline bool DynString<_Tchar>::operator != (const DynString<_Tchar> &rs) const
	{
		return !(*this == rs);
	}
		
	template<class _Tchar>
	inline bool DynString<_Tchar>::operator != (const _Tchar *rs) const
	{
		return !(*this == rs);
	}

	template<class _Tchar>
	inline bool DynString<_Tchar>::operator == (const DynString<_Tchar> &rs) const
	{
		return (rs._length == this->_length &&
			memcmp(this->Chars(), rs.Chars(), sizeof(_Tchar) * this->_length) == 0);
	}
		
	template<class _Tchar>
	inline bool DynString<_Tchar>::operator == (const _Tchar *rs) const
	{
		LargeUInt l = _length + 1;
		const _Tchar *myChars = Chars();

		while(l)
		{
			if(*rs != *myChars)
				return false;
			if(*rs == '\0' && l != 1)
				return false;	// String NULL-terminated, but this has an inline \0
			l--;
			myChars++;
			rs++;
		}
		return true;
	}
		
	template<class _Tchar>
	inline void DynString<_Tchar>::Append(const PanicHandler *ph, const _Tchar *rs)
	{
		Displace(ph, Chars(), _length, rs, CStrLen(rs));
	}
		
	template<class _Tchar>
	inline void DynString<_Tchar>::Append(const PanicHandler *ph, const DynString<_Tchar> &rs)
	{
		Displace(ph, Chars(), _length, rs.Chars(), rs._length);
	}
		
	template<class _Tchar>
	inline void DynString<_Tchar>::Append(const PanicHandler *ph, const _Tchar &rs)
	{
		Displace(ph, Chars(), _length, &rs, 1);
	}

	
	template<class _Tchar>
	inline void DynString<_Tchar>::SetConcat(const PanicHandler *ph, const DynString<_Tchar> &ls, const DynString<_Tchar> &rs)
	{
		Displace(ph, ls.Chars(), ls.Length(), rs.Chars(), rs.Length());
	}
		
	template<class _Tchar>
	inline void DynString<_Tchar>::SetConcat(const PanicHandler *ph, const _Tchar *cstr, const DynString<_Tchar> &rs)
	{
		Displace(ph, cstr, CStrLen(cstr), rs.Chars(), rs.Length());
	}
		
	template<class _Tchar>
	inline void DynString<_Tchar>::SetConcat(const PanicHandler *ph, const DynString<_Tchar> &ls, const _Tchar *cstr)
	{
		Displace(ph, ls.Chars(), ls.Length(), cstr, CStrLen(cstr));
	}
		
	template<class _Tchar>
	inline void DynString<_Tchar>::SetConcat(const PanicHandler *ph, const _Tchar *lcstr, const _Tchar *rcstr)
	{
		Displace(ph, lcstr, CStrLen(lcstr), rcstr, CStrLen(rcstr));
	}

	template<class _Tchar>
	inline LargeUInt DynString<_Tchar>::CStrLen(const _Tchar *chars)
	{
		LargeUInt rv = 0;
		while(*chars)
		{
			chars++;
			rv++;
		}
		return rv;
	}

	template<class _Tchar>
	inline bool DynString<_Tchar>::TryDisplace(const _Tchar *chars1, LargeUInt length1, const _Tchar *chars2, LargeUInt length2)
	{
		_Tchar *baseChars = Chars();
		if(chars1 == baseChars)
		{
			// Concatenating on current
			bool concatSelf = (chars2 == baseChars);

			if(!TryResize(length1 + length2))
				return false;

			if(concatSelf)
				memcpy(Chars() + length1, Chars(), sizeof(_Tchar) * length2);
			else
				memcpy(Chars() + length1, chars2, sizeof(_Tchar) * length2);
		}
		else
		{
			if(!TryResize(length1 + length2))
				return false;
			memcpy(Chars(), chars1, sizeof(_Tchar) * length1);
			memcpy(Chars() + length1, chars2, sizeof(_Tchar) * length2);
		}
		return true;
	}
	
	template<class _Tchar>
	inline void DynString<_Tchar>::Displace(const PanicHandler *ph, const _Tchar *chars1, LargeUInt length1, const _Tchar *chars2, LargeUInt length2)
	{
		if(!TryDisplace(chars1, length1, chars2, length2))
			TDP::Panic(ph, FatalErrors::ERR_AllocationFailed);
	}

	template<class _Tchar>
	inline bool DynString<_Tchar>::TryResize(LargeUInt nChars)
	{
		if(nChars == _length)
			return true;

		if(nChars <= NumStaticChars_k)
		{
			if(_length > NumStaticChars_k)
			{
				memcpy(_staticBuf, _dynBuf, sizeof(_Tchar) * nChars);
				_dynBuf.Release();
			}
			_staticBuf[nChars] = _Tchar(0);
		}
		else
		{
			if(!_dynBuf.TryResize(nChars+1))
				return false;
			if(_length <= NumStaticChars_k)
				memcpy(_dynBuf, _staticBuf, sizeof(_Tchar) * _length);
			_dynBuf[nChars] = _Tchar(0);
		}

		_length = nChars;
		return true;
	}
	
	template<class _Tchar>
	inline const RDX::ObjectManagement::Allocator &DynString<_Tchar>::Allocator() const
	{
		return _alloc;
	}

	template<class _Tchar>
	inline UInt32 DynString<_Tchar>::Hash32(UInt32 hash, const DynString<_Tchar> *str)
	{
		return TDP::Hash32(0, str->Chars(), sizeof(_Tchar) * str->Length());
	}


	typedef DynString<char> CString;
	typedef DynString<wchar_t> UString;
}

#endif
